package il.ac.bgu.cs.heuristics.graph.pddl.parser.types;

import il.ac.bgu.cs.heuristics.utils.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * An immutable representation of a parameter
 * 
 * <p>
 * represents free variables and bound variables(constants)
 * </p>
 * 
 * @author Ronen Pinko
 * 
 */
public abstract class AbstractParameter { // NOPMD generated equals code

	protected final String	name;
	protected Set<Type>		possibleTypes;

	AbstractParameter(String name, Set<Type> possibleTypes) {
		this.name = name;// .toUpperCase(Locale.ENGLISH);
		this.possibleTypes = possibleTypes;
	}

	public boolean ofType(Type t) {
		return possibleTypes.contains(t);
	}

	public Set<Type> getPossibleTypes() {
		return possibleTypes;
	}

	public abstract boolean isFree();

	public boolean isBound() {
		return !isFree();
	}

	public String name() {
		return name;
	}

	@Override
	public String toString() {
		return "(" + name + ":"
				+ StringUtils.toCommaDelimitedString(possibleTypes) + ")";
	}

	public static AbstractParameter newInstance(String name,
			Set<Type> possibleTypes, boolean isFree) {
		return isFree ? new FreeParameter(name, possibleTypes)
				: new BoundParameter(name, possibleTypes);
	}

	public boolean hasSameType(ITypedConstant constant) {
		for (Type t : possibleTypes) {
			if (constant.ofType(t)) {
				return true;
			}
		}
		return false;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	public void addPossiableTypes(Map<Type, List<Type>> typesHierarchy) {
		int setSize = 0;
		do {
			setSize = possibleTypes.size();
			for (Type type : possibleTypes) {
				List<Type> sonsType = typesHierarchy.get(type);
				if (sonsType == null)
					continue;
				for (Type sonType : sonsType) {
					possibleTypes.add(sonType);
				}
			}
		} while (setSize != possibleTypes.size());

	}

	/**
	 * Note: Assuming that same parameter names can't have different types
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		final AbstractParameter other = (AbstractParameter) obj;
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

}
